; >FileCore25

; ======
; GetDir
; ======

; gets directory into directory buffer ALLOWS ESCAPE AND TIMEOUT

; entry: R3 disc address of dir

; exit:  IF error V set, R0 result
;        R5 -> dir buffer, R6 -> byte after dir

GetDir
        Push    "R0-R4,R7-R11,LR"
 [ Debug6
        DREG    R3, "GetDir "
 ]
        MOV     R10,#0  ;FindDir/GetDir Flag
        B       FindDirCommon


; =======
; FindDir
; =======

; given disc address of dir returns ptr to RAM copy, in dir buffer or cache
; ALLOWS ESCAPE AND TIMEOUT

; Calling FindDir will give parameters for the directory, unexpanded, in the cache
; if it was in the cache.

; entry: R3 disc address of dir

; exit:  IF error V set, R0 result
;        R5 -> dir, R6 -> byte after dir

FindDir ROUT
        Push    "R0-R4,R7-R11,LR"
 [ Debug6
        DREG    R3,"FindDir "
 ]
        MOV     R10,#1  ;FindDir/GetDir Flag

FindDirCommon
 [ DebugC
 BL     SanityCheckDirCache
 ]
 [ Module_Version >= 205
        ; If disc isn't FileCore comlain bitterly
        BL      DiscMustBeFileCore      ;(R3->V,R0)
        BVS     %FT95
 ]
        ; Determine dir type (old/new) and set parameters in R4(size) and R9(DirLastMark)
        BL      TestDir    ;(R3->LR,Z)
        MOVEQ   R4, #NewDirSize
        MOVEQ   R9, #-NewDirLastMark
        MOVNE   R4, #OldDirSize
        MOVNE   R9, #-OldDirLastMark

        ; Set up R5(start) and R6(end) for DirBuffer
        sbaddr  R5, DirBuffer
        ADD     R6, R4, R5

 [ Debug6
        DREG    R5, "DirBuffer start, end:",cc
        DREG    R6, ","
 ]

        ; If BufDir matches then no further work to do
        LDR     LR, [SB,#:INDEX:BufDir]
        CMPS    R3, LR
        BEQ     %FT90           ;V=0

        ; Different operation on find/get dir
        MOVS    R10,R10
        BEQ     %FT05           ;if GetDir

        ; FindDir - if found in cache set up parameters for dir in the cache,
        ; otherwise drop through to the getdir case
        BL      TryCache        ;(R3->R11,V)
        ADDVC   R5, R11,#CacheBody
        LDRVC   R6, [R11,#CacheNext]
        ADDVC   R6, SB, R6
        BVC     %FT90           ;If FindDir and in cache

05
        ; GetDir or failed to FindDir in cache - cache the dir in dirbuf, keeping
        ; directory R3 around.
        BL      TryCacheDirBuf  ;(R3)

        ; Different operation on find/get dir
        MOVS    R10,R10
        BNE     %FT65           ;FindDir

        ; GetDir
        ; if we find the dir in the cache
        BL      TryCache        ;(R3->R11,V)
        BVS     %FT65           ;not cached

        ; COPY DIR BODY
        ADD     R0, R11,#CacheBody
        MOV     R1, R5
        LDR     R8, [R11,#CacheNext]
        ADD     R8, SB, R8
        SUB     R7, R8, R0      ;size cached dir
        SUB     R2, R7, R9      ;size cached dir excluding tail
        BL      BlockMove       ;(R0,R1,R2)

        ; FILL MIDDLE WITH ZEROES
        ADD     R0, R1, R2
        SUB     R1, R4, R7      ;amount less than full size
        BL      ZeroRam         ;(R0,R1)

        ; COPY DIR TAIL
        SUB     R0, R8, R9
        SUB     R1, R6, R9
        MOV     R2, R9
        BL      BlockMove       ;(R0,R1,R2)

        ; Remove the directory from the cache
 [ FileCache
        BL      LockDirCache
 ]
        BL      RemoveCacheDir  ;(R11) remove cache copy first
        BL      ValidateDirCache
 [ FileCache
        BL      UnlockDirCache
 ]
        B       %FT85

65
        ; Failed to find directory we want in the cache so
        ; read dir to buffer at R5
        MOV     R7, R3          ;save dir disc address
        MOV     R8, R5          ;save dir buffer ptr

 [ Debug6
        DREG    R5, "Going to read dir to start, end:",cc
        DREG    R6, ","
 ]

        MOV     R1, #ReadSecsOp ;ALLOW ESCAPE AND TIMEOUT
        MOV     R2, R3
        MOV     R3, R5
        BL      IndDiscOp       ;(R1-R4->R0,R3-R5,V)
        BVS     %FT95
        MOV     R5, R8
        MOV     R3, R7          ;restore dir disc add

 [ Debug6
        DREG    R5, "After read DirBuffer start, end:",cc
        DREG    R6, ","
 ]

        BL      TestDir              ;(R3->LR,Z)
        BEQ     %FT85                   ;if new format
        BL      TestDirCheckByte        ;(R3,R5,R6->LR,Z)
        STRB    LR, [R6,#DirCheckByte]  ;set check byte if old format
85
        STR     R3, BufDir      ;validate dir buffer
90
 [ Debug6
        DREG    R5, "Checking dir: DirBuffer start, end:",cc
        DREG    R6, ","
 ]
        BL      CheckDir        ;check well formed (R3,R5,R6->R0,V)
95
        STRVS   R0, [SP]
 [ Debug6
        DLINE   "start   |end     |result - leaving FindDir/GetDir"
        DREG    R5,,cc
        DREG    R6,,cc
        BVC     %FT01
        DREG    R0,,cc
01
        DLINE   ""
 ]
 [ DebugC
 BL     SanityCheckDirCache
 ]
        Pull    "R0-R4,R7-R11"
        B        PullLinkKeepV


; ========
; TryCache
; ========

; see if dir is in cache

; entry R3 = ind disc address of dir

; exit V=0 if found
;  R11 -> cache obj

TryCache ROUT
        Push    "R0,R1,LR"
 [ DebugC
        DREG    R3, "TryCache:"
 ]
 [ FileCache
        BL      LockDirCache
 ]
        BL      CheckDirCache
        sbaddr  R11,RootCache
        MOV     R1, R11
10
        LDR     R11,[R11,#CacheOlder]
        ADD     R11,SB,R11
        TEQS    R11,R1
        BEQ     %FT20
        LDR     LR, [R11,#CacheDir]
        CMPS    LR, R3
        BNE     %BT10
                                 ;FOUND NOW MAKE YOUNGEST V=0
        BL      RemoveCacheDir          ;(R11)
        BL      ValidateDirCache
        BL      InsertCacheDir          ;(R11)
        B       %FT25
20
        BL      SetV
25
 [ FileCache
        BL      UnlockDirCache
 ]
 [ DebugC
        DREG    R3,,cc
        DREG    R11,,cc
        BVS     %FT01
        DLINE   "TryCache succeeded"
        B       %FT02
01
        DLINE   "Trycache failed"
02
 ]
        Pull    "R0,R1,PC"

 [ DebugC
; ===================
; SanityCheckDirCache
; ===================

; entry: nothing

; exit: info about trashed dirs in the cache displayed

SanityCheckDirCache ROUT
        Push    "r0-r12,lr"

        DLINE   "Sanity checking DirCache"

        MOV     r2, #0


        sbaddr  R11,RootCache
        MOV     R1, R11
10
        LDR     R11,[R11,#CacheOlder]
        ADD     R11,SB,R11
        TEQS    R11,R1
        BEQ     %FT20
        LDR     r3, [R11,#CacheDir]
        BL      TestDir    ;(R3->LR,Z)
        ADD     r5, r11, #CacheBody
        LDR     r6, [r11, #CacheNext]
        ADD     r6, r6, SB
        BL      CheckDir
        BVC     %BT10

        DREG    r3, "Cached dir is trashed: ",cc
        DREG    r5, " at ",cc
        DREG    r6, " to "

        ADD     r2, r2, #1

        BNE     %BT10

20
        ; Finished the cache
        TEQ     r2, #0
        BNE     %FT30
        DLINE   "DirCache clean"
        B       %FT40
30
        DREG    r2, "Total trashed dirs:"
40
        Pull    "r0-r12,pc",,^
 ]

 [ F

; ===========
; CacheDirBuf
; ===========

; cache the dir in the buffer
; leaves dir buffer empty

CacheDirBuf ROUT
        Push    "R3,LR"
        MOV     R3, #-1         ;invalid dir address
        BL      TryCacheDirBuf
        Pull    "R3,PC",,^

 ]

; ==============
; TryCacheDirBuf
; ==============

; cache the dir in the buffer if it would not involve losing dir R3 from cache
; leaves dir buffer empty

TryCacheDirBuf ROUT
        Push    "R0-R11,LR"
        MOV     R7, R3          ;favoured directory
        LDR     R3, BufDir
 [ DebugC
        DREG    R3, "TryCacheDirBuf ",cc
        DREG    R7
 ]
        CMPS    R3, #-1
        BEQ     %FT95           ;buffer empty
 [ FileCache
        BL      LockDirCache
 ]
        BL      TestDir      ;(R3->LR,Z)
        MOVEQ   R4, #NewDirSize
        MOVEQ   R9, #-NewDirLastMark
        MOVNE   R4, #OldDirSize
        MOVNE   R9, #-OldDirLastMark
        sbaddr  R5, DirBuffer
        ADD     R6, R4, R5
 [ DebugC
        BL      CheckDir        ;(R3,R5,R6->R0,V)
        BVC     %FT01           ;don't cache bad dirs
 DLINE  "Dir trashed"
 CLRV
01
 ]
        BL      CheckDirCache
                         ;FIND END OF DIR CONTENTS, SO SIZE NEEDED KNOWN
        BL      EndDirEntries   ;(R3,R5,R6->R0)
        SUB     R8, R0, R5      ;size for dir body
        ADD     R4, R8, R9      ;add size for dir tail
        ANDS    LR, R4, #3      ;ensure length is whole number of words
        RSBNE   LR, LR, #4
        ADDNE   R4, R4, LR
        ADD     R4, R4, #CacheBody;add cache overhead
        Push    "R8,R9"
                         ;LOOK FOR SMALLEST SPACE BIG ENOUGH
10
 [ DebugC
        DREG    R4, "Size needed in cache "
 ]
        MOV     R9, #-1         ;rogue smallest length big enough
        sbaddr  R11,DirCache
15
        LDR     LR, [R11,#CachePriority]
        TEQS    LR, #0
        BNE     %FT25           ;not a free space
        MOV     R10,R11
20                              ;spaces are rejoined when searching for
        LDR     R10,[R10,#CacheNext]    ;a space, not when space returned
        ADD     R10,SB, R10
        LDR     LR, [R10,#CachePriority]
        TEQS    LR, #0
        BEQ     %BT20           ;another space

        SUB     LR, R10,SB
        STR     LR, [R11,#CacheNext]
        SUB     R10,R10,R11
        SUBS    LR, R10,R4
 [ DebugC
        DREG    R11,"size=",cc
        DREG    R10," size=",cc
        DREG    LR," extra="
 ]
 [ DebugC
;        BNE     %FT01
;        DLINE   "exact cache fit"
 ]
        BEQ     %FT65           ;exact match
        CMPHSS  LR, #CacheMin
        CMPHSS  R9, R10
        MOVHS   R9, R10         ;if best so far note length
        MOVHS   R8, R11         ;and position
25
        LDR     R11,[R11,#CacheNext]
        ADDS    R11,SB, R11
        BPL     %BT15           ;loop until all objects examined

        CMPS    R9, #-1
        BNE     %FT60           ;gap big enough found

 [ DebugC
        DLINE   "need to free space in cache"
 ]

 [ FileCache            ;HERE CONSIDER RETURNING SPACE FROM FILE CACHE
        LDRB    R9, Interlocks
        ORR     LR, R9, #FileCacheLock
        STRB    LR, Interlocks

        ASSERT  FileBufsEnd = FileBufsStart + 4
        sbaddr  R11,FileBufsStart
        LDMIA   R11,{R11,LR}
        CMPS    R11,LR
        BHS     %FT29           ;no space allocated to file cache

        BL      LessValid
        LDRB    LR, [R11,#BufPriority]
        TEQS    LR, #0
        BEQ     %FT26                   ;first buffer is empty
        LDR     LR, [R11,#BufFcb]
        LDR     LR, [LR, #FcbExtHandle]
        TEQS    LR, #0
        BNE     %FT28                   ;first buffer is allocated to open file
        BL      UnlinkFileChain         ;(R11)
26
        BL      UnlinkAllocChain        ;(R11)
        ADD     R10,R11,#1*K+BufferData
        STR     R10,FileBufsStart
        LDRB    LR, UnclaimedFileBuffers
        ADD     LR, LR, #1
        STRB    LR, UnclaimedFileBuffers
        MOV     LR, #bit31
        MOV     R8, #bit31
        STMDB   R10!,{R8,LR}
        SUB     R8, R10,SB
        MOV     LR, #0
        STMDB   R11,{R8,LR}
28
        BL      MoreValid
29
        STRB    R9, Interlocks
 ]

 [ DebugC
        DLINE   "assign priorities to cache dirs ...",cc
 ]

        sbaddr  R11,RootCache   ;assign priority values to all cached dirs
        MOV     R10,R11
        MOV     R9, #1 :SHL: 31 ;high priority value for favoured dir
        MOVS    LR, #0
        B       %FT35
30
        ADD     LR, LR, #1
        LDR     R8, [R11,#CacheDir]
        TEQS    R8, R7
        STREQ   R9, [R11,#CachePriority]
        STRNE   LR, [R11,#CachePriority]
35
        LDR     R11,[R11,#CacheYounger]
        ADD     R11,SB, R11
        TEQS    R11,R10                 ;loop until back to root
        BNE     %BT30
 [ DebugC
        DLINE   " done"
 ]
                 ;now find desired space with min loss of priority
        sbaddr  R10,DirCache    ;start ptr
        MOV     R11,R10         ;end ptr
        MOV     R8, #-1         ;init min loss of priority
        MOV     R9, #0          ;init priority loss
40                                      ;increase end ptr until space required
        LDR     LR, [R11,#CachePriority]
        ADD     R9, R9, LR              ;total up priority cost
        LDR     R11,[R11,#CacheNext]
        ADDS    R11,SB, R11
        BMI     %FT50                   ;all options checked
        SUB     R1, R11,R10
 [ DebugC
        DREG    R10,"start=",cc
        DREG    R11," end=",cc
        DREG    R1," size=",cc
 ]
        SUBS    R1, R1, R4
 [ DebugC
        DREG    R1, " extra="
 ]
        CMPGTS  R1, #CacheMin
        BLT     %BT40
45                                      ;increase start ptr while enough space
        LDR     LR, [R10,#CacheNext]
        ADD     LR, SB, LR
 [ DebugC
        DREG    LR, "end=",cc
 ]
        SUB     LR, LR, R10
        SUBS    R1, R1, LR
 [ DebugC
        DREG    LR, " size=",cc
        DREG    R1, " extra="
 ]
        CMPGTS  R1, #CacheMin
        LDRGE   R2, [R10,#CachePriority] ;decrease priority cost accordingly
        SUBGE   R9, R9, R2
        ADDGE   R10,R10,LR
        BGE     %BT45

        CMPS    R9, R8          ;if priority cost is lower than best so far
        MOVLO   R8, R9          ;note cost
        MOVLO   R0, R10         ;and position
 [ DebugC
        DREG    R8, "best=",cc
        DREG    R9," fit=",cc
        DREG    R10," start=",cc
        DREG    R11," end="
 ]
        B       %BT40

50                      ;HERE WHEN ALL SPACE MAKING OPTIONS TRIED
        CMPS    R8, #1 :SHL: 31 ;IF caching dir in buffer would force uncaching
        Pull    "R8,R9",HS      ;of favoured dir, or no room
        BHS     %FT90           ;THEN dont cache it
        MOV     R11,R0
55                                      ;uncache chosen dirs
        LDR     LR, [R11,#CachePriority]
        TEQS    LR, #0
        BLNE    RemoveCacheDir          ;(R11)
        BLNE    ValidateDirCache
        LDR     R11,[R11,#CacheNext]
        ADD     R11,SB,R11
        SUB     LR, R11,R0
        SUBS    LR, LR, R4
        CMPGTS  LR, #CacheMin
        BLT     %BT55
 [ DebugC
        DLINE   "try again now cache space freed"
 ]
        B       %BT10           ;now try again, will succeed as enough now free

60                              ;HERE IF SPACE BIGGER THAN REQUIRED FOUND
        MOV     R11,R8
        ADD     R10,R11,R4              ;start of extra part
        LDR     LR, [R11,#CacheNext]    ;set up extra part as new obj
        STR     LR, [R10,#CacheNext]
        MOV     LR, #0
        STR     LR, [R10,#CachePriority]
        SUB     LR, R10,SB
        STR     LR, [R11,#CacheNext]    ;and amend desired part
65                              ;EXACT SPACE REJOINS HERE
        Pull    "R8,R9"
        MOV     R0, R5
        ADD     R1, R11,#CacheBody
        ADD     R2, R8, #1              ;copy dir body and terminating zero
        BL      BlockMove               ;(R0,R1,R2)

        SUB     R0, R6, R9              ;copy dir tail to cache
        ADD     R1, R11,R4
        SUB     R1, R1, R9
        MOV     R2, R9
        BL      BlockMove

        BL      InvalidateBufDir        ;to avoid possibility of same dir in
        STR     R3, [R11,#CacheDir]     ;buffer and cache, buffer must be
        BL      InsertCacheDir          ;invalidated first
90
 [ FileCache
        BL      UnlockDirCache
 ]
        BL      InvalidateBufDir
95
 [ DebugC
 BL     SanityCheckDirCache
 ]
        Pull    "R0-R11,PC",,^


; =============
; CheckDirCache
; =============

; If cache validity flag is clear reinitialise cache
 [ FileCache
; dir cache must be locked
 ]

CheckDirCache ROUT
        Push    "R0-R1,LR"
        LDRB    LR, Flags
        ANDS    LR, LR, #CacheGood
        BNE     %FT95
 [ DebugC
        DLINE   "Reinitialise cache"
 ]
        MOV     R0, #:INDEX:RootCache   ;init root of dual linked list
        STR     R0, RootCache+CacheYounger
        STR     R0, RootCache+CacheOlder
        sbaddr  R0, DirCache            ;create single free space obj
        STR     LR, [R0,#CachePriority] ;LR=0
 [ FileCache
        LDR     R1, FileBufsStart
 |
        LDR     R1, MapSpace
        ADD     R1, SB, R1
 ]
        SUB     R1, R1, #CacheMin       ;->rogue end obj
        SUB     LR, R1, SB
        STR     LR, [R0,#CacheNext]
        MOV     LR, #1 :SHL: 31         ;create rogue end of cache obj
        STR     LR, [R1,#CacheNext]
        STR     LR, [R1,#CachePriority]
        BL      ValidateDirCache
95
        Pull    "R0-R1,PC",,^


; ==============
; RemoveCacheDir
; ==============

; entry R11 -> cache entry
; leaves dir cache invalid

RemoveCacheDir ROUT
        Push    "R0-R2,LR"
 [ DebugC
        LDR     R0,[R11,#CacheDir]
        DREG    R0, "RemoveCacheDir ",cc
        DREG    R11
 ]
        LDR     R0, [R11,#CacheYounger]
        ADD     R1, SB, R0
        LDR     R2, [R11,#CacheOlder]
        BL      InvalidateDirCache
        ADD     LR, SB, R2
        STR     R0, [LR,#CacheYounger]
        STR     R2, [R1,#CacheOlder]
        MOV     LR, #0
        STR     LR, [R11,#CachePriority]
        Pull    "R0-R2,PC",,^


; ==============
; InsertCacheDir
; ==============

; entry R11 -> cache entry

InsertCacheDir ROUT
        Push    "R0-R3,LR"
 [ DebugC
        LDR     R0, [R11,#CacheDir]
        DREG    R0,"InsertCacheDir ",cc
        DREG    R11
 ]
        MOV     R0, #:INDEX:RootCache
        ADD     R1, SB, R0
        LDR     R2, [R1,#CacheOlder]
        ADD     R3, SB, R2
        BL      InvalidateDirCache
        SUB     LR, R11, SB
        STR     R0, [R11,#CacheYounger]
        STR     R2, [R11,#CacheOlder]
        STR     LR, [R1,#CacheOlder]
        STR     LR, [R3,#CacheYounger]
        STR     R11,[R11,#CachePriority];restore non zero priority
        BL      ValidateDirCache
        Pull    "R0-R3,PC",,^


; ===========
; UnCacheDisc
; ===========

; Remove all directories of given disc from cache

; entry: R0 = disc rec num

UnCacheDisc ROUT
 [ DebugC
        DREG    R0,"UnCacheDisc"
 ]
        Push    "R0,R1,R11,LR"
        LDRB    LR, Flags               ;don't re init cache as used by buffer
        TSTS    LR, #CacheGood          ;for single drive backup
        BEQ     %FT95
        MOV     R0, R0, LSL #(32-3)
        sbaddr  R11,RootCache
        MOV     R1, R11
        B       %FT20
10
        LDR     LR, [R11,#CacheDir]
        EOR     LR, LR, R0
        ANDS    LR, LR, #DiscBits
        BLEQ    RemoveCacheDir  ;(R11)
        BLEQ    ValidateDirCache
20
        LDR     R11,[R11,#CacheOlder]
        ADD     R11,SB,R11
        TEQS    R11,R1
        BNE     %BT10
95
        Pull    "R0,R1,R11,PC",,^

; ==================
; InvalidateDirCache
; ==================

InvalidateDirCache ROUT
        Push    "LR"
        LDRB    LR, Flags
        BIC     LR, LR, #CacheGood
        STRB    LR, Flags
        Pull    "PC"


; ================
; ValidateDirCache
; ================

ValidateDirCache ROUT
        Push    "LR"
        LDRB    LR, Flags
        ORR     LR, LR, #CacheGood
        STRB    LR, Flags
        Pull    "PC"

 [ FileCache

; ============
; LockDirCache
; ============

LockDirCache
        Push    "LR"
 [ DebugC :LOR: DebugG
        DLINE   "LockDirCache"
 ]
        LDRB    LR, Interlocks
        ORR     LR, LR, #DirCacheLock
        STRB    LR, Interlocks
        Pull    "PC",,^


; ==============
; UnlockDirCache
; ==============

UnlockDirCache
        Push    "LR"
 [ DebugC :LOR: DebugG
        DLINE   "UnlockDirCache"
 ]
        LDRB    LR, Interlocks
        BIC     LR, LR, #DirCacheLock
        STRB    LR, Interlocks
        Pull    "PC",,^


; ===============
; ExtendFileCache
; ===============

;entry FileCache must be claimed
;exit If could extend FileCache by one buffer NE and R11 -> buffer

ExtendFileCache
        Push    "R0-R4,R8,R10,LR"
        TEQP    PC, #I_bit :OR: SVC_mode        ;disable IRQ
        LDRB    R0, Interlocks
        EOR     R0, R0, #DirCacheLock
        TSTS    R0, #DirCacheLock
        LDRNEB  LR, Flags
        TSTNES  LR, #CacheGood
        TEQEQP  PC, #Z_bit :OR: SVC_mode        ;if EQ reenable IRQ maintaining Z=1
        BEQ     %FT55
        STRB    R0, Interlocks  ;lock dir cache
        TEQP    PC, #SVC_mode   ;reenable IRQ
        LDR     R8, FileBufsStart
        SUB     R8, R8, #1*K
        SUB     R8, R8, #BufferData+CacheMin
 [ DebugC :LOR: DebugG
        DLINE   ">ExtendFileCache - lock dir cache"
        DREG    R8, "new LWM="
 ]
00
;LOOP TO FIND LAST DIR IN CACHE
        sbaddr  R11,RootCache
        MOV     R10,R11
        MOV     R0, R11
05
        LDR     R11,[R11,#CacheYounger]
        ADD     R11,SB, R11
        CMPS    R11,R0
        MOVHI   R0, R11
        TEQS    R11,R10
        BNE     %BT05

        TEQS    R0, R10
        sbaddr  R10,DirCache, EQ
        LDRNE   R10,[R0,#CacheNext]
        ADDNE   R10,SB, R10
        SUBS    LR, R8, R10
        CMPGTS  LR, #CacheMin
 [ DebugC :LOR: DebugG
        BLT     %FT01
        DREG    R10,"no obstructing dir "
        B       %FT02
01
        DREG    R0, "dir must be moved for extension ",cc
        DREG    R10
02
 ]
        BGE     %FT45

10
        LDRB    LR, ReEntrance  ;cant be done if foregound threaded in
        TEQS    LR, #0
        MOVNE   LR, #IRQsema
        LDRNE   LR, [LR]        ;and entering from background
        TEQNES  LR, #0
        TOGPSR  Z_bit, LR
        BEQ     %FT50

        SUB     R2, R10,R0              ;dir size
 [ DebugC :LOR: DebugG
        DREG    R2,"dir size "
 ]

;LOOP TO FIND SMALLEST GAP SUITABLE TO MOVE DIR TO
        MOV     R3, #bit30      ;large +ve
        sbaddr  R11,DirCache
        B       %FT20

15
        LDR     R11,[R11,#CacheNext]
        ADDS    R11,SB, R11
        BMI     %FT30           ;dummy end obj
20
        LDR     LR, [R11,#CachePriority]
        TEQS    LR, #0
        BNE     %BT15           ;not a free space
        MOV     R10,R11
25
        LDR     R11,[R11,#CacheNext]
        ADD     R11,SB, R11
        LDR     LR, [R11,#CachePriority]
        TEQS    LR, #0
        BEQ     %BT25           ;another space

        SUB     LR, R11,SB
        STR     LR, [R10,#CacheNext]

        SUB     R4, R11,R10     ;gap length
        SUBS    LR, R4, R2
        CMPGTS  LR, #CacheMin
        CMPGES  R3, R4
        BLT     %BT20           ;gap wrong size

        SUBS    LR, R8, R11     ;If gap does not overlap extension
        CMPGTS  LR, #CacheMin
        MOVGE   R1, R10         ; note position
        MOVGE   R3, R4          ; note size
        B       %BT20

30
        TEQS    R3, #bit30
 [ DebugC :LOR: DebugG
        BEQ     %FT01
        DREG    R1,"gap big enough found ",cc
        DREG    R3
01
 ]
        BNE     %FT40

        sbaddr  R11,RootCache
        MOV     R10,R11
        MOV     R0, R11
35
        LDR     R11,[R11,#CacheYounger]
        ADD     R11,SB,R11

        LDR     R1, [R11,#CacheNext]
        ADD     R1, SB, R1
        SUB     R3, R1, R11     ;dir length
        SUBS    LR, R3, R2
        CMPGTS  LR, #CacheMin
38
        BLGE    RemoveCacheDir  ;(R11) leaves dir cache invalid
        BGE     %BT05

        CMPS    R11,R0
        MOVHI   R0, R11
        B       %BT35

40
        BL      InvalidateDirCache
        LDR     R11,[R1,#CacheNext]
        BL      BlockMove       ;(R0-R2) copy to new position
        ADD     R4, R1, R2      ;new dir end
        SUB     LR, R4, SB
        STR     LR, [R1,#CacheNext]
        TEQS    R2, R3
        ASSERT  CacheNext=0
        ASSERT  CachePriority=CacheNext+4
        MOV     LR, #0
        STMNEIA  R4,{R11,LR}                    ;gap header if spare fragment
        STR     LR, [R0,#CachePriority]         ;mark dir block free
        LDR     R0, [R1,#CacheYounger]          ;adjust links
        ADD     R0, SB, R0
        LDR     R2, [R1,#CacheOlder]
        ADD     R2, SB, R2
        SUB     R1, R1, SB
        STR     R1, [R0,#CacheOlder]
        STR     R1, [R2,#CacheYounger]
        BL      ValidateDirCache
        B       %BT00

45
        BL      InvalidateDirCache
        SUB     LR, R8, SB              ;adjust length of final gap
        STR     LR, [R10,#CacheNext]
        ASSERT  CacheNext=0                     ;build new dummy end
        ASSERT  CachePriority=CacheNext+4
        MOV     R11,#bit31
        MOVS    LR, #bit31                      ;also set NE
        STMIA   R8!,{R11,LR}
        STR     R8, FileBufsStart               ;update start
        MOV     R11,R8

50
        BL      ValidateDirCache
        BL      UnlockDirCache
55
 [ DebugC :LOR: DebugG
        BNE     %FT01
        DLINE   "extend failed"
        B       %FT02
01
        DREG    R11,,cc
02
        DLINE   "<ExtendFileCache"
 ]
        Pull    "R0-R4,R8,R10,PC"
 ]


; ================
; FormDirCheckByte
; ================

; entry
;  R5 -> dir start
;  R6 -> dir end
;  R7 -> dir tail

; exit  LR check byte, flags corrupt

FormDirCheckByte ROUT
        Push    "R0-R2,R5,R7,LR"
        ADD     R0, R5, #DirFirstEntry
        B       %FT05

; ================
; TestDirCheckByte
; ================

; entry
;  R3 ind disc add of dir
;  R5 -> dir start
;  R6 -> dir end

; exit
;  LR check byte
;  Z  clear if matches existing byte

TestDirCheckByte
        Push    "R0-R2,R5,R7,LR"
        BL      EndDirEntries                   ;(R3,R5,R6->R0)
        BL      TestDir                         ;(R3->LR,Z)
        ADDEQ   R7, R6, #NewDirLastMark+1       ;dir tail
        ADDNE   R7, R6, #OldDirLastMark+1
05
        BIC     R1, R0, #3
        MOV     R2, #0
10                              ;whole words before end of entries
        LDR     LR, [R5],#4
        EOR     R2, LR, R2,ROR #13
        TEQS    R5, R1
        BNE     %BT10
20                              ;odd bytes before end of entries
        LDRNEB  LR, [R5], #1            ;not first pass through loop
        EORNE   R2, LR, R2,ROR #13
        TEQS    R5, R0
        BNE     %BT20

        MOV     R5, R7
30                              ;odd bytes before at start of tail
        TSTS    R5, #3
        LDRNEB  LR, [R5],#1
        EORNE   R2, LR, R2, ROR #13
        BNE     %BT30

        ASSERT  DirCheckByte=-1         ;dont include last word as it contains
        SUB     R1, R6, #4              ;the check byte
40                              ;whole words in tail
        LDR     LR, [R5],#4
        EOR     R2, LR, R2,ROR #13
        TEQS    R5, R1
        BNE     %BT40

        EOR     R2, R2, R2, LSR #16     ;reduce to 8 bits
        EOR     R2, R2, R2, LSR #8
        AND     R2, R2, #&FF

        LDRB    LR, [R6,#DirCheckByte]  ;compare with old check byte
        TEQS    R2, LR
        MOV     LR, R2

        Pull    "R0-R2,R5,R7,PC"


; =============
; EndDirEntries
; =============

; Find the end of the list of entries in a dir

; entry
;  R3 ind disc add of dir
;  R5 -> dir start
;  R6 -> dir end

; exit
;  R0 -> first empty entry

EndDirEntries ROUT
 [ NewDirEntrySz=OldDirEntrySz
        Push    "R2,LR"
        BL      TestDir                 ;(R3->LR,Z)
        ADDEQ   R2, R6, #NewDirLastMark
        ADDNE   R2, R6, #OldDirLastMark
        ADD     R0, R5, #DirFirstEntry
        SUB     R0, R0, #NewDirEntrySz
10                              ;loop examining entries
        LDRB    LR, [R0,#NewDirEntrySz] !
        CMPS    LR, #0                  ;until null entry
        CMPNES  R0, R2                  ;or exhausted
        BLO     %BT10
        MOVHI   R0, R2
        Pull    "R2,PC",,^
 |
        Push    "R1-R2,LR"
        BL      TestDir                 ;(R3->LR,Z)
        MOVEQ   R1, #NewDirEntrySz
        MOVNE   R1, #OldDirEntrySz
        ADDEQ   R2, R6, #NewDirLastMark
        ADDNE   R2, R6, #OldDirLastMark
        ADD     R0, R5, #DirFirstEntry
        SUB     R0, R0, R1
10                              ;loop examining entries
        LDRB    LR, [R0,R1] !
        CMPS    LR, #0                  ;until null entry
        CMPNES  R0, R2                  ;or exhausted
        BLO     %BT10
        MOVHI   R0, R2
        Pull    "R1-R2,PC",,^
 ]

        LTORG
        END
